#pragma once

/*实现异步工作器具体的工作方案*/
/*条件变量, 生产&消费
    双缓冲区
    互斥锁
    生产缓冲区没有数据, 处理完消费缓冲区后就休眠.
    回调函数,(针对缓冲区中数据的处理接口-外界传入一个函数, 告诉异步工作器如何处理
    提供的操作:
    1\停止异步工作器的接口,
    添加数据到缓冲区里面,即向写入缓冲区写入数据
    私有操作,就是创建线程, 线程入口函数中,交换缓冲区, 对消费缓冲区数据使用回调函数进行处理, 处理完后再次交换.
    */
#include <iostream>
#include <functional>
#include <thread>
#include "Buffer.hpp"
#include <memory>
#include <condition_variable>
#include <atomic>
#include <mutex>
namespace mian_yang
{
    using Functor = std::function<void(Buffer &)>;
    enum AsyncType
    {
        /*安全状态， 表示缓冲区满了就阻塞， 避免资源耗尽的风险*/
        ASYNC_SAFE,
        /*不考虑资源耗尽问题， 无限扩容，常用于测试*/
        ASYNC_UNSAFE
    };

    class AsyncLooper
    {
    public:
        using ptr = std::shared_ptr<AsyncLooper>;

        AsyncLooper(const Functor &cb, AsyncType loop_type = AsyncType::ASYNC_SAFE)
            : _stop(false),
              _thread(std::thread(&AsyncLooper::threadEntry, this)),
              _callBack(cb),
              _looper_type(loop_type) /*默认是安全状态，缓冲区不够了， 就阻塞*/
        {
        }
        ~AsyncLooper()
        {
            stop();
        }

        void stop()
        {
            _stop = true;
            /*将推出标志位置为true*/
            _cond_con.notify_all();
            /*等待工作线程的退出*/
            _thread.join();
        }

        void push(const char *data, size_t len)
        {
            /*无限扩容 -- 非安全*/
            /*固定大小 -- 生产缓冲区中满了就阻塞*/
            std::unique_lock<std::mutex> lock(_mutex); /*加锁*/
            /*条件变量控制， 若缓冲区剩余空间大小大于数据长度。 则可以添加数据*/
            if (_looper_type == AsyncType::ASYNC_SAFE)
            {
                _cond_pro.wait(lock, [&]()
                               { return _pro_buf.WriteAbleSize() >= len; });
            }

            /*能够走下来， 就说明满足了条件， 可以像缓冲区中添加数据。 */
            _pro_buf.push(data, len);

            /*唤醒消费者对缓冲区中的数据进行处理.因为此时消费者可能因为原本生产者没数据的情况陷入了休眠*/
            _cond_con.notify_one();
        }

        void threadEntry()
        {
            // while (!_stop)b  退出循环通过stop为真和_pro_buf为空同时成立 才结束
            while (1)
            {
                /*
                    判断生产缓冲区有没有数据， 有则交换， 无则阻塞
                */
                {
                    /*加锁*/
                    std::unique_lock<std::mutex> lock(_mutex);

                    /*
                        如果当前是退出前被唤醒，
                    或者有数据被唤醒， 则返回真，
                    继续向下运行， 否则继续进入休眠
                    */
                   /*如果不退出(大部分情况), 就要生产者有数据.然后拿到数据处理完成.将生产者唤醒,等待生产者下次再有数据*/
                    _cond_con.wait(lock, [&]()
                                   { return _stop || !_pro_buf.empty(); });
                    /*退出标记被设置， 且生产缓冲区一没有数据，
                    这时候再退出， 否则可能造成生产缓冲区中有数据，
                    但是没有被处理。
                    */
                    if (_stop && _pro_buf.empty())
                        break;

                    _con_buf.swap(_pro_buf); /*生产缓冲区和消费缓冲区进行交换。 */
                    /*如果是保证安全性， 那么就直接唤醒生产者就可以了。
                    因为此时生产者和消费者之间已经没有数据了。*/
                    if (_looper_type == AsyncType::ASYNC_SAFE)
                    {
                        /*唤醒生产者*/
                        _cond_pro.notify_all();
                    }
                }
                /*处理，被唤醒后， 对缓冲区进行数据处理*/
                _callBack(_con_buf);

                /*初始化消费缓冲区*/
                _con_buf.reset();
            }
        }

    private:
        /*具体对缓冲区数据进行处理的回调函数， 由异步工作其使用者提供*/
        Functor _callBack;

    private:
        AsyncType _looper_type;            /*异步日志器类别*/
        std::atomic<bool> _stop;           /*是否停止标记位*/
        Buffer _pro_buf;                   /*生产缓冲区*/
        Buffer _con_buf;                   /*消费缓冲区*/
        std::mutex _mutex;                 /*互斥锁， 保证线程安全问题*/
        std::condition_variable _cond_pro; /*生产者等待队列*/
        std::condition_variable _cond_con; /*消费者等待队列*/
        std::thread _thread;               /*异步工作器的工作线程*/
    };
}
